ViewRoot.php

<?php

namespace Fresh;

class ViewRoot {

    use \Fresh\Readonly;

    protected $file;
    private $r_file;

    protected $name;
    private $r_name;

    protected $src;
    private $r_src;

    protected $handler;
    private $r_handler;

    protected $compFile;
    protected $passthru = [];
    private $r_passthru;

    protected $setupCode = [];

    protected $component;
    private $r_component;

    /**
     * 
     * 
     */
    public function __construct($component,$viewName,$passthru=[]){
        $this->component = $component;
        $this->file = $this->fileFor($viewName);
        $this->name = $viewName;
        $this->passthru = $passthru;
        $this->handler = new \Fresh\Handler();

        $this->addRuntimeHandler('compileAndAddResources',[$this,'compileResourcesAndOutsource']);
        $this->src = file_get_contents($this->file);

        $this->compFile = $this->compileFileFor($viewName);

        $this->initialize();
    }

    public function compileResourcesAndOutsource(){
        $c = 0;
        $list = [
            'css'=>[],
            'js'=>[],
            'php'=>[]
        ];
        $name = basename($this->file);
        // $name = pathinfo($this->name,PATHINFO_FILENAME);
        $name = substr($name,0,strrpos($name,'.'));

        $dir = dirname($this->file);
        $path = $dir.'/'.$name;

        $cssFile = $path.'.css';
        
        if (file_exists($cssFile)){
            $list['css'][] = $cssFile;
            $c++;
        }

        $jsFile = $path.'.js';
        
        if (file_exists($jsFile)){
            $list['js'][] = $jsFile;
            $c++;
        }
        

        
        $resourceDir = $path.'/';
        if (is_dir($resourceDir)){
            $rii = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($resourceDir));
            $files = array(); 
            foreach ($rii as $file) {
                if ($file->isDir()){ 
                    continue;
                }
                $c++;
                $list[$file->getExtension()][] = $file->getPathname(); 
            }
        }
        if ($c>0){
            /**
             * If your view has any resources associated, you must register an addResources handler.
             * - `addResources`: `function($viewObj, $resourcesList){...use your framework or whatever...}
            *    - `$resourcesList` is in the format ['php'=>$arrayOfFiles,'js'=>$arrayOfFiles,'css'...] & so on for EVERY file that is associated with your view.
            *    - These files are found at `YourViewNameView/some_file.EXTENSION`
            *       - I think you can do YourViewNameView.css and YourViewNameView.js as well
            *       - It also works with YourViewNameForm/files....
             * 
             * @export(Handler.Runtime.addResources)
             */
            $this->callHandler('addResources',$this,$list);
        }
    }

    protected function fileFor($viewName){
        if ($viewName=='')$viewName = 'View';
        $file = $this->component->dir.'/view/'.$viewName.'.php';
        return $file;
    }
    protected function compileFileFor($viewName){
        return $this->component->dir.'/compiled/'.$viewName.'.php';
    }
    
    public function form(){
        $form = $this->component->form($this->name);
        return $form;
    }
    public function view(){
        $view = $this->component->view($this->name);
        return $view;
    }




    public function setPassthru($passthru){
        $this->passthru = $passthru;
    }

    public function addFormatter($name,$callback){
        $this->handler->setHandler('runtime','format_'.$name,$callback);
    }
    public function addQuery($xpath,$callable){
        $this->handler->addHandler('query','all',['xpath'=>$xpath,'callback'=>$callable]);
    }
    public function addCompileHandler($name,$callable){
        $this->handler->addHandler('compile',$name,$callable);
    }
    /**
     * Calling $viewRoot->executeCompileHandlers($handlerName, ...$argsForHandler) will return an array of return values from the called handlers.
     * 
     * @export(Internals.executeCompileHandlers)
     */
    protected function executeCompileHandlers($handlerName, ...$args){
        // throw new \Exception("working on handlers....");

        $handlers = $this->handler->compile->$handlerName;//[$handlerName] ?? null;
        // print_r($handlerName);
        // throw new \Exception("execute compile handlers blah blah");
        // exit;
        if ($handlers===null)return;
        $ret = [];
        foreach ($handlers as $handler){
            $ret[] = $handler(...$args);//$this,$rbAttrs,$prop,$attribute->value);
        }
        return $ret;
    }
    public function addRuntimeHandler($name,$callable){
        $this->handler->addHandler('runtime',$name,$callable);
    }
    public function setRuntimeHandler($name,$callable){
        $this->handler->setHandler('runtime',$name,$callable);
    }

    public function callHandler($method,...$args){
        return $this->handler->callMethod('runtime',$method,$args);
    }

    public function compile($forceRecompile=false){
        $file = $this->file;
        $compFile = $this->compFile;
        if (!$forceRecompile&&
            true&&
            file_exists($compFile)&&filemtime($file)<filemtime($compFile)){
            return $compFile;
        }

        $viewContent = file_get_contents($file);
        // echo $viewContent."\n\n";
        $compiler = new \Taeluf\PHTML\Compiler();
        $cleanSource = $compiler->cleanSource($viewContent);
        $preDocHandlers = $this->handler->compile->preDoc ?? [];
        foreach ($preDocHandlers as $handler){
            $cleanSource = $handler($cleanSource,$viewContent,$compiler,$this);
        }

        $doc = new \Taeluf\PHTML($cleanSource);
        $compiler->appendCode($doc);

        $queryResults = [];
        // foreach ($this->handler->query->all as $qi){
        //     echo $qi['xpath']."\n\n";
        // }   
        // // print_r($this->handler->query->all);
        // exit;
        $qRes = [];
        foreach ($this->handler->query->all as $queryInfo){
            //TODO ...maybe... perform all queries before calling any handlers
            $xpath = $queryInfo['xpath'];
            $callback = $queryInfo['callback'];

            // $xp = new \DOMXpath($doc);
            // $nodeList = $xp->query($xpath);
            $nodeList = $doc->xpath($xpath);
            $qRes[] = [
                'callback'=>$callback,
                'xpath'=>$xpath,
                'nodes'=>$nodeList
            ];
        }
        foreach ($qRes as $qi){
            foreach ($qi['nodes'] as $node){
                $qi['callback']($doc,$this,$compiler,$node);
            }
        }
        // $output = $compiler->output();
        // echo "output:\n\n";
        // echo $output;
        // echo "\n\n\n----done";
        // exit;
        $compiler->prependCode('<?php $this->callHandler(\'compileAndAddResources\');?>');

        $compiler->writeTo($compFile,0660);
        $this->writeSetupCode();
        return $compFile;
    }

    /**
     * Need to stop blindly extracting passthru, probably...
     * 
     * @extract(TODO.ViewPassthru)
     */
    protected function output(){
        $compiledFile = $this->compile();
        extract($this->passthru);
        // var_dump($id);
        // exit;
        ob_start();
        require($compiledFile);
        return ob_get_clean();
    }

    public function __toString(){
        $output = $this->output();
        return $output;
    }

    public function writeSetupCode(){
        $compDir = dirname($this->compFile).'/setup/';
        $setupFile = $compDir.basename($this->compFile);

        if (!is_dir($compDir)){
            mkdir($compDir,0771,true); 
        }
        file_put_contents($setupFile,implode('',$this->setupCode));
    }

    public function appendSetupCode($code){
        $this->setupCode[] = trim($code);
    }

    public function setup($passthru=[]){
        $compDir = dirname($this->compFile).'/setup/';
        $setupFile = $compDir.basename($this->compFile);
        extract($passthru);
        if (file_exists($setupFile))require($setupFile);
    }

}